package connectfour;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/**
 * GUI class for connect 4.
 * 
 * @author Antonio Rios
 * 
 */

public class GUI extends JPanel implements MouseListener {

	private Image boardbg = Toolkit.getDefaultToolkit().getImage(
			"board tile.png");
	private Coords pixelCoords[][]; // 1 to 1 correspondence with board array
	private Coords move = null;
	private int pieceSize = 44; // size of a game piece, in px
	private JFrame frame;
	public JTextArea console = new JTextArea(
			"Welcome to Connect 4\nPlayer 1 is red, Player 2 is blue\n", 10, 30);
	private int[][] board;
	private int columnClicked = -1;

	private int numRows;
	private int numCols;

	// player colors
	private Color p1c = new Color(255, 0, 0);
	private Color p2c = new Color(0, 0, 255);
	private Color pEc = new Color(200, 200, 200);
	private Color bg = new Color(160, 140, 100);

	// game type - 2 = AIvAI, 1 = AIvP, 0 = PvP
	int gt;

	/**
	 * Constructor. Takes in a board, as defined by the board class.
	 * 
	 * @param int[][]
	 *            board
	 */
	public GUI(int[][] b, int numRows, int numCols, int cn) {
		// set rows and columns
		this.numRows = numRows; // 6
		this.numCols = numCols; // 7
		
		this.frame = new JFrame(this.numCols + " by " + this.numRows + " Connect " + cn);

		this.pixelCoords = new Coords[this.numRows][this.numCols];
		// set preferred size based on desired board size
		// this.setPreferredSize(new Dimension(350+10+16, 300+10+16));
		this.setPreferredSize(new Dimension((this.numCols * 50) + 10 + 16,
				(this.numRows * 50) + 10 + 16));

		// set the board
		this.board = b;

		// generate pixel coordinates for each possible tile location
		this.generateCoords();

		// display the gui
		this.createAndDisplayGUI();

	}

	/**
	 * Get the player's next move (human player only)
	 * 
	 * @return int column
	 */
	public int getNextMove() {
		// while columnClicked is -1 keep trying to get their move
		this.columnClicked = -1;
		while (this.columnClicked == -1) {
			// wait for click
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {

			}

		}
		if (this.move == null) {
			return -1;
		} else {
			return this.columnClicked;
		}

	}

	/**
	 * Create and display the window
	 */
	public void createAndDisplayGUI() {
		// grid bag layout
		GridBagLayout g = new GridBagLayout();
		frame.setLayout(g);
		GridBagConstraints c = new GridBagConstraints();

		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// add board panel
		c.fill = GridBagConstraints.BOTH;
		c.weighty = 0.5;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		c.gridx = 0;
		c.gridy = 0;
		frame.add(this, c);

		console.setEditable(false);
		JScrollPane jsp = new JScrollPane(console);

		// add the console
		c.fill = GridBagConstraints.BOTH;
		c.weighty = 0;
		c.gridx = 0;
		c.gridy = 1;
		c.anchor = GridBagConstraints.NORTH;

		frame.add(jsp, c);
		frame.addMouseListener(this);
		frame.pack();
		frame.setVisible(true);
		frame.setResizable(false);
	}

	/**
	 * Refresh the gui
	 */
	public void updateDisplay() {
		console.setCaretPosition(console.getText().length());
		this.repaint();
	}

	public void updateDisplay(Board board) {
		this.board = board.getBoard();
		console.setCaretPosition(console.getText().length());
		this.repaint();
	}

	/**
	 * Show the winner via dialog box. Takes the player number as the parameter
	 * 
	 * @param winner
	 */
	public void showWinner(int winner) {
		if (winner == 0) {
			// player 1 wins
			JOptionPane.showMessageDialog(frame, "Player 1 wins!",
					"We have a winner!", JOptionPane.PLAIN_MESSAGE);
		} else if (winner == 1) {
			// player 2 wins
			JOptionPane.showMessageDialog(frame, "Player 2 wins!",
					"We have a winner!", JOptionPane.PLAIN_MESSAGE);
		} else {
			// draw
			JOptionPane.showMessageDialog(frame, "It's a draw!", "",
					JOptionPane.PLAIN_MESSAGE);
		}
	}

	/**
	 * This class describes a set of coordinates. Used to keep track of where to
	 * start drawing a piece. Values are in pixels
	 */
	private class Coords {
		private int x, y;

		public Coords(int a, int b) {
			x = a;
			y = b;
		}

		public int getX() {
			return x;
		}

		public int getY() {
			return y;
		}
	}

	/**
	 * Generates the possible starting coordinates for where we can start
	 * drawing each piece. Values are in pixels.
	 */
	private void generateCoords() {
		int x = 16, y = 16; // initial values
		int increment = 50; // increment by 54 px everytime

		for (int i = 0; i < this.numRows; i++) {
			for (int j = 0; j < this.numCols; j++) {
				pixelCoords[i][j] = new Coords(x, y);
				x = x + increment;
			}
			x = 16;
			y = y + increment;
		}
		// System.out.println("x: " + x + ", y: " + y);
	}

	/**
	 * get the column in which the click occurred. takes the x coordinate of the
	 * click in pixels, returns the column
	 * 
	 * @param int
	 *            click
	 * @return int column
	 */
	private int getMoveColumn(int click) {
		int x = 18; // starting horizontal position
		int increment = 50; // amount to increment by in the checks
		int delta = 5; // how much slack we give till we hit the next column
		int leftBounds = x - delta;
		int rightBounds = x + this.pieceSize + delta;
		int j = 0;

		// check which column the click occured
		for (j = 0; j < this.numCols; j++) {
			if ((click >= leftBounds) && (click <= rightBounds)) {
				// j is our colum, simply break, keeping j intact
				// System.out.println("col" + j);
				break;
			}

			x = x + increment;
			leftBounds = x - delta;
			rightBounds = x + this.pieceSize + delta;
		}

		if (j >= this.numCols) {
			j = this.numCols - 1;
		}

		return j;
	}

	/**
	 * Helper method that returns the pixel coords of a move given the column
	 * 
	 * @param int
	 *            col
	 * @return Coords pixelCoordinates
	 */
	private Coords getMoveCoords(int col) {
		// check in what row the highest piece in that column is in
		for (int k = this.numRows - 1; k >= 0; k--) {
			if (this.board[k][col] == -1) {
				return this.pixelCoords[k][col];
			}
		}

		// if we get to this point, no move is made
		return null;
	}

	/**
	 * Get the coords of the click, check what column its in and place the piece
	 * appropriately.
	 * 
	 * @param MousEvent
	 *            e
	 */
	public void mouseClicked(MouseEvent e) {
		// check in what column the click happened
		int click = this.getMoveColumn(e.getX());
		this.move = this.getMoveCoords(click);
		this.columnClicked = click;

		if (this.move == null) {
			console
					.append("Move was not made - column is full.  Please try again\n");
			console.setCaretPosition(console.getText().length());
		} else {
			this.repaint();
		}

		Thread t = Thread.currentThread();
		t.interrupt();
	}

	// do nothing on all these mouse events
	public void mousePressed(MouseEvent e) {
	}

	public void mouseReleased(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	/**
	 * Paint the pieces and draw the board
	 */
	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		// paint bg
		g.setColor(this.bg);
		g.fillRect(0, 0, this.getWidth(), this.getHeight());

		// draw the board array
		for (int x = 0; x < this.numRows; x++) {
			for (int y = 0; y < this.numCols; y++) {

				// choose the appropriate color
				if (board[x][y] == 0) {
					g.setColor(p1c);
				} else if (board[x][y] == 1) {
					g.setColor(p2c);
				} else if (board[x][y] == -1) {
					g.setColor(pEc);
				}
				// draw the piece
				g.fillRect(this.pixelCoords[x][y].getX(),
						this.pixelCoords[x][y].getY(), pieceSize, pieceSize);
				g.drawImage(boardbg, this.pixelCoords[x][y].getX(),
						this.pixelCoords[x][y].getY(), this);

			}
		}

	}

}
